﻿using FarseerGames.FarseerPhysics.Mathematics;
using FarseerGames.FarseerPhysics;
using Microsoft.Xna.Framework.Graphics;
using Rectangle = Microsoft.Xna.Framework.Rectangle;
using XnaVector2 = Microsoft.Xna.Framework.Vector2;
using Microsoft.Xna.Framework.Audio;
using FarseerGames.FarseerPhysics.Collisions;
using CymbergajTouch.Logic;
using System;

namespace CymbergajTouch.Match
{
    /// <summary>
    /// Puck class.
    /// </summary>
    class Puck : Pusher, GoalHandler
    {
        //Information needed to play sound effects
        private SoundBank soundBank;
        private Pusher leftPusher;
        private Pusher rightPusher;
        private Pusher extraPusher;

        //Match options
        private Options options;

        //Textures
        private Texture2D normalTexture;
        private Texture2D bonusTexture;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="physicsSimulator">Farseer physics simulator</param>
        /// <param name="position">initial puck position</param>
        public Puck(PhysicsSimulator physicsSimulator, Vector2 position, Options options)
            : base(physicsSimulator, position, null)
        {
            physicsSimulator.Remove(this.body);
            physicsSimulator.Remove(this.geom);
            this.speed = 0.0001f;
            this.radiusDenominator = 17;
            this.radius = IceRink.iceHeight / radiusDenominator;
            this.body = CreateBody(position);
            this.body.RotationalDragCoefficient = 0.9f;
            physicsSimulator.Add(this.body);
            this.geom = CreateGeom();
            geom.OnCollision = new CollisionEventHandler(OnCollision);
            physicsSimulator.Add(this.geom);

            this.options = options;
        }

        public new void Draw(SpriteBatch spriteBatch)
        {
            XnaVector2 originVector = new XnaVector2(texture.Width / 2.0f, texture.Height / 2.0f);
            //It works, but it's a bit weird that radius must be used to calculate destination rectangle
            Rectangle destinationRectangle = new Rectangle((int)(geom.Position.X - geom.AABB.Width / 2 + radius),
                (int)(geom.Position.Y - geom.AABB.Height / 2 + radius),
                (int)geom.AABB.Width, (int)geom.AABB.Height);
            spriteBatch.Draw(texture, destinationRectangle, null, Color.White, body.Rotation, originVector, SpriteEffects.None, 0);
        }

        public void GoalScored(object sender, bool left)
        {
            Vector2 newPosition = new Vector2(IceRink.iceWidth * (left ? 1 : 3) / 4, IceRink.iceHeight / 2);
            Random random = new Random();
            while (CollidesWithPushers(newPosition))
            {
                newPosition.Y = random.Next((int)Math.Ceiling(radius), (int)Math.Ceiling(IceRink.iceHeight - radius));
            }
            body.Position = newPosition;
            body.LinearVelocity = new Vector2(0, 0);
        }

        /// <summary>
        /// Checks if puck at new position collides with any pusher.
        /// </summary>
        /// <param name="newPosition">new puck position</param>
        /// <returns>true if collides, otherwise false</returns>
        private bool CollidesWithPushers(Vector2 newPosition)
        {
            double leftPusherDistance = Math.Sqrt((newPosition.X - leftPusher.body.Position.X) * (newPosition.X - leftPusher.body.Position.X) +
                (newPosition.Y - leftPusher.body.Position.Y) * (newPosition.Y - leftPusher.body.Position.Y));
            if (leftPusherDistance <= radius + leftPusher.radius)
            {
                return true;
            }

            double rightPusherDistance = Math.Sqrt((newPosition.X - rightPusher.body.Position.X) * (newPosition.X - rightPusher.body.Position.X) +
                (newPosition.Y - rightPusher.body.Position.Y) * (newPosition.Y - rightPusher.body.Position.Y));
            if (rightPusherDistance <= radius + rightPusher.radius)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Sets sound bank for playing voices.
        /// </summary>
        /// <param name="soundBank">sound bank</param>
        public void SetSoundBank(SoundBank soundBank)
        {
            this.soundBank = soundBank;
        }

        /// <summary>
        /// Sets geoms of pushers, needed in collision detection.
        /// </summary>
        /// <param name="pusher1Geom">first pusher's geom</param>
        /// <param name="pusher2Geom">second pusher's geom</param>
        public void SetPushers(Pusher leftPusher, Pusher rightPusher, Pusher extraPusher)
        {
            this.leftPusher = leftPusher;
            this.rightPusher = rightPusher;
            this.extraPusher = extraPusher;
        }

        /// <summary>
        /// Sets puck's texture for indicating bonus opportunity.
        /// </summary>
        /// <param name="bonusTexture">bonus texture</param>
        public void SetBonusTexture(Texture2D bonusTexture)
        {
            this.bonusTexture = bonusTexture;
            this.normalTexture = texture;
        }

        /// <summary>
        /// Changes puck's texture to normal.
        /// </summary>
        public void ChangeToNormal()
        {
            texture = normalTexture;
        }

        /// <summary>
        /// Changes puck's texture to bonus.
        /// </summary>
        public void ChangeToBonus()
        {
            texture = bonusTexture;
        }

        /// <summary>
        /// Method for on-collision event, used to play collision sounds.
        /// </summary>
        /// <param name="geom1">first body geom</param>
        /// <param name="geom2">second body geom</param>
        /// <param name="contactList">unused parameter, forced by inheritence</param>
        /// <returns>true value in order to be able to receive next events</returns>
        private bool OnCollision(Geom geom1, Geom geom2, ContactList contactList)
        {
            if (!options.Sound)
            {
                return true;
            }

            if (geom1 == leftPusher.geom || geom1 == rightPusher.geom || geom1 == extraPusher.geom ||
                geom2 == leftPusher.geom || geom2 == rightPusher.geom || geom2 == extraPusher.geom)
            {
                soundBank.PlayCue("hit");
            }
            else
            {
                soundBank.PlayCue("cling");
            }

            //we indicate, that we want to receive the collision events
            return true;
        }
    }
}
